Русский

Раскройте возможности федерации GraphQL с помощью сшивания схем. Узнайте, как создать единый GraphQL API из нескольких сервисов, улучшая масштабируемость и поддерживаемость.

Федерация GraphQL: Сшивание схем (Schema Stitching) — подробное руководство

В постоянно меняющемся ландшафте современной разработки приложений необходимость в масштабируемых и поддерживаемых архитектурах стала первостепенной. Микросервисы, с их присущей модульностью и независимостью развертывания, стали популярным решением. Однако управление множеством микросервисов может привести к сложностям, особенно когда речь идет о предоставлении единого API для клиентских приложений. Именно здесь на помощь приходит федерация GraphQL, а в частности — сшивание схем (Schema Stitching).

Что такое федерация GraphQL?

Федерация GraphQL — это мощная архитектура, которая позволяет создать единый, унифицированный API GraphQL из нескольких нижележащих сервисов GraphQL (часто представляющих собой микросервисы). Она позволяет разработчикам запрашивать данные из разных сервисов так, как если бы это был один граф, упрощая работу на стороне клиента и снижая потребность в сложной логике оркестрации.

Существует два основных подхода к федерации GraphQL:

Эта статья посвящена сшиванию схем (Schema Stitching), в ней рассматриваются его концепции, преимущества, ограничения и практическая реализация.

Понимание сшивания схем

Сшивание схем — это процесс объединения нескольких схем GraphQL в единую, целостную схему. Эта унифицированная схема действует как фасад, скрывая сложность нижележащих сервисов от клиента. Когда клиент отправляет запрос к сшитой схеме, шлюз интеллектуально направляет запрос к соответствующему сервису (или сервисам), извлекает данные и объединяет результаты перед их возвращением клиенту.

Представьте это так: у вас есть несколько ресторанов (сервисов), каждый из которых специализируется на разных кухнях. Сшивание схем — это как универсальное меню, которое объединяет все блюда из каждого ресторана. Когда клиент заказывает из универсального меню, заказ интеллектуально направляется на соответствующие кухни ресторанов, еда готовится, а затем объединяется в одну доставку для клиента.

Ключевые концепции сшивания схем

Преимущества сшивания схем

Сшивание схем предлагает несколько весомых преимуществ для организаций, внедряющих микросервисную архитектуру:

Ограничения сшивания схем

Хотя сшивание схем предлагает множество преимуществ, важно осознавать его ограничения:

Практическая реализация сшивания схем

Давайте рассмотрим упрощенный пример реализации сшивания схем с использованием Node.js и библиотеки graphql-tools (популярный выбор для сшивания схем). Этот пример включает два микросервиса: сервис пользователей (User Service) и сервис продуктов (Product Service).

1. Определение удаленных схем

Сначала определим схемы GraphQL для каждого из удаленных сервисов.

Сервис пользователей (user-service.js):


const { buildSchema } = require('graphql');

const userSchema = buildSchema(`
  type User {
    id: ID!
    name: String
    email: String
  }

  type Query {
    user(id: ID!): User
  }
`);

const users = [
  { id: '1', name: 'Alice Smith', email: 'alice@example.com' },
  { id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
];

const userRoot = {
  user: (args) => users.find(user => user.id === args.id),
};

module.exports = {
  schema: userSchema,
  rootValue: userRoot,
};

Сервис продуктов (product-service.js):


const { buildSchema } = require('graphql');

const productSchema = buildSchema(`
  type Product {
    id: ID!
    name: String
    price: Float
    userId: ID!  # Внешний ключ к User Service
  }

  type Query {
    product(id: ID!): Product
  }
`);

const products = [
  { id: '101', name: 'Laptop', price: 1200, userId: '1' },
  { id: '102', name: 'Smartphone', price: 800, userId: '2' },
];

const productRoot = {
  product: (args) => products.find(product => product.id === args.id),
};

module.exports = {
  schema: productSchema,
  rootValue: productRoot,
};

2. Создание сервиса-шлюза

Теперь создадим сервис-шлюз, который будет сшивать две схемы вместе.

Сервис-шлюз (gateway.js):


const { stitchSchemas } = require('@graphql-tools/stitch');
const { makeRemoteExecutableSchema } = require('@graphql-tools/wrap');
const { graphqlHTTP } = require('express-graphql');
const express = require('express');
const { introspectSchema } = require('@graphql-tools/wrap');
const { printSchema } = require('graphql');
const fetch = require('node-fetch');

async function createRemoteSchema(uri) {
  const fetcher = async (params) => {
    const response = await fetch(uri, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(params),
    });
    return response.json();
  };

  const schema = await introspectSchema(fetcher);
  return makeRemoteExecutableSchema({
    schema,
    fetcher,
  });
}

async function main() {
  const userSchema = await createRemoteSchema('http://localhost:4001/graphql');
  const productSchema = await createRemoteSchema('http://localhost:4002/graphql');

  const stitchedSchema = stitchSchemas({
    subschemas: [
      { schema: userSchema },
      { schema: productSchema },
    ],
    typeDefs: `
      extend type Product {
        user: User
      }
    `,
    resolvers: {
      Product: {
        user: {
          selectionSet: `{ userId }`,
          resolve(product, args, context, info) {
            return info.mergeInfo.delegateToSchema({
              schema: userSchema,
              operation: 'query',
              fieldName: 'user',
              args: {
                id: product.userId,
              },
              context,
              info,
            });
          },
        },
      },
    },
  });

  const app = express();
  app.use('/graphql', graphqlHTTP({
    schema: stitchedSchema,
    graphiql: true,
  }));

  app.listen(4000, () => console.log('Gateway server running on http://localhost:4000/graphql'));
}

main().catch(console.error);

3. Запуск сервисов

Вам нужно будет запустить сервис пользователей и сервис продуктов на разных портах. Например:

Сервис пользователей (порт 4001):


const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./user-service');

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true,
}));

app.listen(4001, () => console.log('User service running on http://localhost:4001/graphql'));

Сервис продуктов (порт 4002):


const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./product-service');

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true,
}));

app.listen(4002, () => console.log('Product service running on http://localhost:4002/graphql'));

4. Запрос к сшитой схеме

Теперь вы можете делать запросы к сшитой схеме через шлюз (работающий на порту 4000). Вы можете выполнить такой запрос:


query {
  product(id: "101") {
    id
    name
    price
    user {
      id
      name
      email
    }
  }
}

Этот запрос извлекает продукт с ID "101", а также получает связанного пользователя из сервиса пользователей, демонстрируя, как сшивание схем позволяет запрашивать данные из нескольких сервисов в одном запросе.

Продвинутые техники сшивания схем

Помимо базового примера, существуют некоторые продвинутые техники, которые можно использовать для улучшения вашей реализации сшивания схем:

Выбор между сшиванием схем и Apollo Federation

Хотя сшивание схем является жизнеспособным вариантом для федерации GraphQL, Apollo Federation стала более популярным выбором благодаря своим расширенным функциям и улучшенному опыту разработчиков. Вот сравнение двух подходов:

Функция Сшивание схем Apollo Federation
Определение схемы Использует существующий язык схем GraphQL Использует декларативный язык схем с директивами
Планирование запросов Требует ручного делегирования запросов Автоматическое планирование запросов шлюзом Apollo
Расширения типов Ограниченная поддержка Встроенная поддержка расширений типов
Ключевые директивы Не поддерживается Использует директиву @key для идентификации сущностей
Распределенная трассировка Требует ручной реализации Встроенная поддержка распределенной трассировки
Инструментарий и экосистема Менее зрелый инструментарий Более зрелый инструментарий и большое сообщество
Сложность Может быть сложным в управлении в больших системах Разработано для больших и сложных систем

Когда выбирать сшивание схем:

Когда выбирать Apollo Federation:

Примеры из реального мира и сценарии использования

Вот несколько примеров из реального мира того, как можно использовать федерацию GraphQL, включая сшивание схем:

Лучшие практики для сшивания схем

Чтобы обеспечить успешную реализацию сшивания схем, следуйте этим лучшим практикам:

Заключение

Федерация GraphQL со сшиванием схем предлагает мощный подход к созданию унифицированных API из нескольких сервисов в микросервисной архитектуре. Понимая ее основные концепции, преимущества, ограничения и техники реализации, вы можете использовать сшивание схем для упрощения доступа к данным, улучшения масштабируемости и повышения поддерживаемости. Хотя Apollo Federation стала более продвинутым решением, сшивание схем остается жизнеспособным вариантом для более простых сценариев или при интеграции существующих сервисов GraphQL. Тщательно рассмотрите свои конкретные потребности и требования, чтобы выбрать наилучший подход для вашей организации.